#include "LevelParser.h"
#include "Level.h"
#include "TextureManager.h"
#include "Game.h"
#include "TileLayer.h"
#include "base64.h"
#include "zlib.h"
#include "ObjectLayer.h"
#include "GameObjectFactory.h"
#include "GameObject.h"

Level * LevelParser::parseLevel(const char *levelFile){
    TiXmlDocument levelDocument;
    levelDocument.LoadFile(levelFile);

    Level * pLevel = new Level();
    
    TiXmlElement * pRoot = levelDocument.RootElement();
    TiXmlElement * pProperties = nullptr;
    
    pRoot->Attribute("tilewidth", &m_tileSize);
    pRoot->Attribute("width", &m_width);
    pRoot->Attribute("height", &m_height);
    
    for (TiXmlElement * e = pRoot->FirstChildElement();
            e != nullptr;
            e = e->NextSiblingElement()){
        if (e->Value() == std::string("tileset")){
            parseTilesets(e, pLevel->getTilesets());
        }
    }

    // find propertis
    for (TiXmlElement * e = pRoot->FirstChildElement();
        e != nullptr;
        e = e->NextSiblingElement()
        ){
        if (e->Value() == std::string("properties")){
            pProperties = e;
            break;
        }
    }
    
    for (TiXmlElement * e = pProperties->FirstChildElement();
        e != nullptr;
        e = e->NextSiblingElement()
        ){
        if ( e->Value() == std::string("property")){
            parseTextures(e);
         }
    }


    for(TiXmlElement * e = pRoot->FirstChildElement();
            e != nullptr;
            e = e->NextSiblingElement()){
        if (e->Value() == std::string("objectgroup")
        ||  e->Value() == std::string("layer")){
            if (e->Value() == std::string("objectgroup")){
                parseObjectLayer(e, pLevel->getLayers());
            } else if (e->Value() == std::string("layer")){
                parseTileLayer(e, 
                        pLevel->getLayers(),
                        pLevel->getTilesets(),
                        pLevel->getCollisionLayers()
                        );
            }
        }
        
    }

        


    return pLevel;
}

void LevelParser::parseTilesets(TiXmlElement * pTilesetRoot,
        std::vector<Tileset> * pTilesets){
    TheTextureManager::Instance()->load(
            pTilesetRoot->FirstChildElement()->Attribute("source"),
            pTilesetRoot->Attribute("name"),
            TheGame::Instance()->getRenderer()
            );

    Tileset tileset;
    pTilesetRoot->FirstChildElement()->Attribute("width", &tileset.width);
    pTilesetRoot->FirstChildElement()->Attribute("height", &tileset.height);
    pTilesetRoot->Attribute("firstgid", &tileset.firstGridID);
    pTilesetRoot->Attribute("tilewidth", &tileset.tileWidth);

    pTilesetRoot->Attribute("tileheight", &tileset.tileHeight);
    pTilesetRoot->Attribute("spacing", &tileset.spacing);
    pTilesetRoot->Attribute("margin", &tileset.margin);
    tileset.name = pTilesetRoot->Attribute("name");

    tileset.numColumns = tileset.width / (tileset.tileWidth + tileset.spacing);

    pTilesets->push_back(tileset);

}

void LevelParser::parseTileLayer(TiXmlElement * pTileElement,
    std::vector<Layer*> * pLayers,
    const std::vector<Tileset> * pTilesets,
    std::vector<TileLayer *> *pCollisionLayerList
    )
{
    bool collidable = false;
    TileLayer * pTileLayer = new TileLayer(m_tileSize, *pTilesets);

    std::vector<std::vector<int>> data;

    std::string decodedIDs;
    TiXmlElement * pDataNode;


    for (TiXmlElement * e = pTileElement->FirstChildElement();
            e != nullptr;
            e = e->NextSiblingElement()
            ) {
        if (e->Value() == std::string("properties")){
            for (TiXmlElement * property = e->FirstChildElement();
                    property != nullptr;
                    property = property->NextSiblingElement()
                    ){
                if (property->Value() == std::string("property")){
                    if (property->Attribute("name") ==
                            std::string("collidable")){
                        collidable = true;
                    }
                }
            }    
        }


        if (e->Value() == std::string("data")){
            pDataNode = e;
        }
    }
    
    for (TiXmlNode * e = pDataNode->FirstChild();
            e != nullptr;
            e = e->NextSibling()
            ){
        TiXmlText * text = e->ToText();
        std::string t = text->Value();
        decodedIDs = base64_decode(t);
    }

    uLongf numGids = m_width * m_height * sizeof(int);
    std::vector<unsigned> gids(numGids);
    uncompress(
            (Bytef*)&gids[0], &numGids,
            (const Bytef*)decodedIDs.c_str(), decodedIDs.size()
            );

    std::vector<int> layerRow(m_width);

    for (int j=0; j < m_height; j++){
        data.push_back(layerRow);
    }
    
   for (int rows = 0; rows < m_height; rows++){
        for (int cols= 0; cols < m_width; cols++){
            data[rows][cols] = gids[rows * m_width + cols];
        }
   }

    pTileLayer->setTileIDs(data);

    if (collidable){
        pCollisionLayers->push_back(pTileLayer);
    }

    pLayers->push_back(pTileLayer);
}

void LevelParser::parseTextures(TiXmlElement * pTextureRoot)
{
    TheTextureManager::Instance()->load(
        pTextureRoot->Attribute("value"),
        pTextureRoot->Attribute("name"),
        TheGame::Instance()->getRenderer()
            );
}

void LevelParser::parseObjectLayer(TiXmlElement *pObjectElement,
                    std::vector<Layer*> *pLayers)
{
    ObjectLayer * pObjectLayer = new ObjectLayer();
    std::cout << pObjectElement->FirstChildElement()->Value();

    for (TiXmlElement * e = pObjectElement->FirstChildElement();
            e != nullptr;
            e = e->NextSiblingElement()
        ){
        std::cout << e->Value();
        if (e->Value() == std::string("object")){
            int x, y;
            int width, height;
            int numFrames, callbackID, animSpeed;
            std::string textureID;

            e->Attribute("x", &x);
            e->Attribute("y", &y);
            GameObject * pGameObject =
                TheGameObjectFactory::Instance()->create(
                        e->Attribute("type")
                        );
            for (TiXmlElement * properties = e->FirstChildElement();
                    properties != nullptr;
                    properties = properties->NextSiblingElement()
                    ){
                 if(properties->Value() == std::string("properties")){
                    for(TiXmlElement * property = properties->FirstChildElement();
                        property != nullptr;
                        property = property->NextSiblingElement()
                            ){    
                            if(property->Value() == std::string("property")){
                                if (property->Attribute("name") == std::string("numFrames")){
                                    property->Attribute("value", &numFrames);
                                } else if(property->Attribute("name") == std::string("height")){
                                    property->Attribute("value", &height);
                                } else if(property->Attribute("name") == std::string("texture_id")){
                                    textureID =property->Attribute("value");                                
                                } else if(property->Attribute("name") == std::string("width")){
                                    property->Attribute("value", &width);
                                } else if(property->Attribute("name") == std::string("callbackID")){
                                    property->Attribute("value", &callbackID);
                                } else if(property->Attribute("name") == std::string("animation_speed")){
                                    property->Attribute("value", &animSpeed);
                                }
                            }

                        }
                    }
                 }   
            
            pGameObject->load(
                new LoaderParams(
                    x,y,
                    width, height,
                    textureID, numFrames, callbackID,animSpeed)
                    );
            if (type == "Player"){
                pLevels->setPlayer(dynamic_cast<Player*>(pGameObject));
            }
            pObjectLayer->getGameObjects()->push_back(pGameObject);
        }
    }

    pLayers->push_back(pObjectLayer);
}




























